package replacer;

import java.lang.reflect.Array;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import java.util.HashMap;

import javax.sound.sampled.ReverbType;
import javax.swing.DefaultListModel;

import org.sqlite.Function;

public class Engine {
	private Connection dbh;
	private String driverName = "org.sqlite.JDBC";
    private String url = "jdbc:sqlite:dictionary.db";
    protected static HashMap<Character, String> missMap;
    protected final static String keyMap = "qwertyuiopasdfghjkl;zxcvbnm,.";
    static {
    	missMap = new HashMap<>();
    	missMap.put('a', "a");
    	missMap.put('ą', "a");
    	missMap.put('c', "ć");
    	missMap.put('ć', "c");
    	missMap.put('e', "ę");
    	missMap.put('ę', "e");
    	missMap.put('l', "ł");
    	missMap.put('ł', "l");
    	missMap.put('n', "ń");
    	missMap.put('ń', "n");
    	missMap.put('o', "uó");
    	missMap.put('ó', "uo");
    	missMap.put('s', "ś");
    	missMap.put('ś', "s");
    	missMap.put('u', "ó");
    	missMap.put('z', "źż");
    	missMap.put('ź', "żz");
    	missMap.put('ż', "źz");
    }

	public Engine() throws SQLException {
		__connect();
		
	}
	
    private void __connect() throws SQLException{
        try{
        Class.forName(this.driverName);
        dbh = DriverManager.getConnection(this.url);
        Function.create(dbh, "regexp", new Function() {
			@Override
			protected void xFunc() throws SQLException {
		        String pattern = value_text(0);
		        String keyword = value_text(1);
		        int ret = keyword.matches(pattern)?1:0;
				result(ret);
			}
        });
        
        System.out.println("Pomyślnie połączono z baza danych SQL");
        }
        catch (ClassNotFoundException e) {
             System.out.println("Brak polaczenia.");
             e.printStackTrace();
        }
    }

	public void suggest(String keyword, DefaultListModel<String> suggestions, boolean[] chkbxValues) {
		try {		
			Statement stmt = dbh.createStatement();
			suggestions.clear();
			String sql = null;

			for (int i = 0; i < 5; i++) {
				if (chkbxValues[i]) {
					switch (i) {
					case 0:
						sql = getSQLMissingLetter(keyword);
						break;
					case 1:
						sql = getSQLExtraLetter(keyword);
						break;
					case 2:
						sql = getSQLSwichedLetters(keyword);
						break;
					case 3:
						sql = getSQLMisspelling(keyword);
						break;
					case 4:
						sql = getSQLTypo(keyword);
						break;
					default:
						break;
					}
					System.out.println(sql);
					ResultSet rs = stmt.executeQuery(sql);
					while (rs.next()) {
						if(suggestions.contains(rs.getString("word"))) continue;
						suggestions.addElement(rs.getString("word"));
					}
				}
			}
			System.out.println(suggestions.getSize());
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	
	private String getSQLMissingLetter(String keyword) {
		char[] list = keyword.toCharArray();
		StringBuilder builder = new StringBuilder();
	    builder.append( ".?");
		for (int i=0; i < list.length; i++) {
		    builder.append( list[i]);
		    builder.append(".?");
		}
		return "SELECT word FROM polish WHERE length(word)="+Integer.toString(keyword.length()+1)+" AND regexp('"+builder.toString()+"', word)";//'" + builder.toString() + "'";
	}
	
	private String getSQLExtraLetter(String keyword) {
		char[] list = keyword.toCharArray();
		StringBuilder builder = new StringBuilder();
		for (int i=0; i < list.length; i++) {
		    builder.append( "(" + list[i] + ")?");
		}
		return "SELECT word FROM polish WHERE length(word)="+Integer.toString(keyword.length()-1)+" AND regexp('"+builder.toString()+"', word)";//'" + builder.toString() + "'";
	}
	
	private String getSQLSwichedLetters(String keyword) {		
		char[] str = keyword.toCharArray();
		StringBuilder builder = new StringBuilder();
		for (int i=1; i < keyword.length(); i++) {
			swap(str, i-1, i);
		    builder.append(" word = '" + new String(str) + "'");
		    if(i < keyword.length()-1)
		    	builder.append(" OR");
			swap(str, i-1, i);
		}
		return "SELECT word FROM polish WHERE"+builder.toString();
	}
	
	private String getSQLMisspelling(String keyword) {
		char[] str = keyword.toCharArray();
		StringBuilder builder = new StringBuilder();
		String missStr;
		char key;
		boolean notStart = false;
		for (int i=0; i < keyword.length(); i++) {
			missStr = missMap.get(str[i]);
			if(missStr == null) continue;
			key = str[i];
			for(char c : missStr.toCharArray()) {
			    if(notStart) builder.append(" OR");
				notStart = true;
				str[i] = c;
			    builder.append(" word = '" + new String(str) + "'");			
			}
			str[i] = key;
		}		
		
		return "SELECT word FROM polish WHERE"+builder.toString();
	}
	
	private String getSQLTypo(String keyword) {
		char[] str = keyword.toCharArray();
		StringBuilder builder = new StringBuilder();
		String typoKeys;
		char key;
		boolean notStart = false;
		for (int i=0; i < keyword.length(); i++) {
			typoKeys = getNeighbours(str[i]);
			key = str[i];
			for(char c : typoKeys.toCharArray()) {
			    if(notStart) builder.append(" OR");
				notStart = true;
				str[i] = c;
			    builder.append(" word = '" + new String(str) + "'");			
			}
			str[i] = key;
		}			
		
		
		return "SELECT word FROM polish WHERE"+builder.toString();
	}
	
	private String getNeighbours(char key) {
		StringBuffer result = new StringBuffer();
		for (char c : keyMap.toCharArray()) {
		if (c != key && distance(c,key)<2) {
		result.append(c);
		} 
		}
		return result.toString();
	}

	private double distance(char c, char key) {
		return Math.sqrt(Math.pow(colOf(key)-colOf(c),2)+Math.pow(rowOf(key)-rowOf(c),2));
		}

	private int rowOf(char c) {
		 return keyMap.indexOf(c)/10;
		 
	}
	private static int colOf(char c) {
		return keyMap.indexOf(c)%10;
	}
	
	private void swap(char[] str, int i, int j) {			
		char temp = str[i];
		str[i] = str[j];
		str[j] = temp;
	}
}
